# -*- coding: utf-8 -*-
"""
--------------------------------------------
    Author:  niuqt
    Date:  9/14/2022 
--------------------------------------------
"""
import os
import re
import threading
import time

import paramiko
import json

from django.core.paginator import EmptyPage, Paginator
from django.db.models import Q
from django.http import HttpResponse, JsonResponse

# 日志根目录
from django.shortcuts import render

from app01 import models
from app01.rsacrypto import RsaCrypto

LOG_BASE_DIR = "/"
# 每一页显示几条
PAGE_SIZE = 10
# 分页数
PAGE_NUM_PAGES = 11


def view_dir(request):
    """
    查看目录列表
    :param request:
    :return:
    """
    ip = request.GET.get('ip')

    if not ip:
        return HttpResponse("主机不能为空")

    # 判断主机是否存活
    cmd = "whoami"
    result = ssh3(ip, cmd)
    # print("result", result,type(result))

    if not result or result['status'] != 0:
        return HttpResponse("错误，主机: {}，ssh连接失败！".format(ip))

    dir = request.GET.get('dir')
    if not dir:
        dir = ''

    # 搜索关键字
    key = request.GET.get('search')
    # print("key",key)
    # 判断关键字是否存在
    if key:
        log_list = get_search_log_list(ip, dir, key)
        # print(log_list)
        # 分页a标签的herf前缀
        herf_prefix = "?ip={}&dir={}&search={}".format(ip, dir, key)
    else:
        log_list = get_log_list(ip, dir)
        # 分页a标签的herf前缀
        herf_prefix = "?ip={}&dir={}".format(ip, dir)

    # print("log_list",log_list)
    # log_list = get_log_list(ip, dir)
    if log_list is False:
        return HttpResponse("获取目录列表失败")
    elif log_list == []:
        return HttpResponse("目录列表为空")
    elif log_list == {}:
        return HttpResponse("搜索结果为空")
    else:
        pass

    dir_path_temp = os.path.join(LOG_BASE_DIR, dir)  # 结果 //data/wsshOps/venv
    dir_path = dir_path_temp.replace("//", "/")
    # print(dir_path)
    # next_dir_path = os.path.join(dir_path,dir)

    # 分页
    paginator = Paginator(log_list, PAGE_SIZE)  # 每页显示指定的条数

    # 异常判断
    try:
        # 当前页码，如果取不到page参数，默认为1
        current_num = int(request.GET.get("page", 1))  # 当前页码
        log_list = paginator.page(current_num)  # 获取当前页码的数据
    except EmptyPage:  # 页码不存在时,报EmptyPage错误
        log_list = paginator.page(1)  # 强制更新为第一页

    #  如果页数十分多时，换另外一种显示方式
    if paginator.num_pages > PAGE_NUM_PAGES:  # 一般网页展示11页,左5页,右5页,加上当前页,共11页
        if current_num - 5 < 1:  # 如果前5页小于1时
            pageRange = range(1, PAGE_NUM_PAGES)  # 页码的列表:范围是初始状态
        elif current_num + 5 > paginator.num_pages:  # 如果后5页大于总页数时
            # 页码的列表:范围是(当前页-5,总页数+1)。因为range顾头不顾尾,需要加1
            pageRange = range(current_num - 5, paginator.num_pages + 1)
        else:
            # 页码的列表:后5页正常时,页码范围是(当前页-5,当前页+6)。注意不是+5,因为range顾头不顾尾！
            pageRange = range(current_num - 5, current_num + 6)
    else:
        pageRange = paginator.page_range  # 页码的列表

    data = {
        "paginator": paginator, "current_num": current_num, "pageRange": pageRange, "herf_prefix": herf_prefix,
        "ip": ip,
        "dir_path": dir_path,
        "dir": dir,
        "log_list": log_list,
        # "host_all": host_all
    }
    return render(request, 'rsync/dirs.html', data)


class BaseResponse(object):
    """
    返回response
    """

    def __init__(self):
        self.code = 200
        self.data = None
        self.error = None

    @property
    def dict(self):
        return self.__dict__


'''
response = BaseResponse()  # 默认状态
object
    code:200    # 前端代码判断
    data:None    # 前端渲染页面
    error:None   # 前端错误展示

{'code':1000,'data':None,'error':None}
'''

CODE = {
    "200": "正常",
    "500": "目录已经存在",
    "501": "删除失败",
    "502": "上传失败",
    "503": "请求方式非法",
    "504": "远程scp失败",
    "505": "远程解压文件失败",
    "506": "用户名不存在",
    "507": "更新密码失败",
}


def ssh3(ip, cmd, seconds=10):
    """
    使用ssh连接远程服务器执行命令
    :param cmd: 执行的命令
    :param seconds: 执行命令的超时时间
    :return: list
    """
    result = {'status': 1, 'data': None}  # 返回结果
    try:

        # print(ip)
        remote_user_id = models.RemoteHost.objects.filter(ip=ip).values('remote_user_id')[0]['remote_user_id']
        remote_port = models.RemoteHost.objects.filter(ip=ip).values('port')[0]['port']
        remote_user_name = models.RemoteUser.objects.filter(name=remote_user_id).values('username')[0]['username']
        remote_user_password = models.RemoteUser.objects.filter(name=remote_user_id).values('password')[0]['password']

        # print(remote_user_id,remote_port,remote_user_name,remote_user_password)

        r_password_decrypt = RsaCrypto().decrypt(remote_user_password)['message']  # 解密 password

        ssh_client = paramiko.SSHClient()  # 实例化一个
        ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())  # 相当于 linux的 自动添加 主机策略
        ssh_client.connect(hostname=ip, port=remote_port, username=remote_user_name, password=r_password_decrypt)

    except Exception as e:

        pass

    else:
        stdin, stdout, stderr = ssh_client.exec_command(cmd, get_pty=True)
        Stdout_xx = stdout.readlines()
        # print(Stdout_xx)

        channel = stdout.channel  # # 执行状态,0 表示成功，1 表示失败
        status_values = channel.recv_exit_status()

        # print(status_values)

        # 修改返回结果
        result['status'] = status_values
        result['data'] = Stdout_xx
        return result

    finally:
        ssh_client.close()  # 关闭连接


def get_log_list(host, dir):
    """
    日志列表
    :param host: ip地址
    :param dir: 目录名
    :return: list
    """

    # print("LOG_BASE_DIR",settings.LOG_BASE_DIR)
    dir_path_temp = os.path.join(LOG_BASE_DIR, dir)
    dir_path = dir_path_temp.replace("//", "/")  # 浏览器 输入框的 地址
    # print("dir_path",dir_path)
    # ls -lht可以查看文件大小以M来显示
    # --time-style '+%Y/%m/%d %H:%M:%S' 自定义显示方式
    cmd = "ls -lht --time-style '+%Y-%m-%d %H:%M:%S' {}".format(dir_path)
    # print("cmd",cmd,"host",host)
    # result = execute_linux(cmd)
    result = ssh3(host, cmd)

    # print("result", result,type(result))
    ## {'status': 0, 'data': ['总用量 48K\r\n', 'dr-xr-xr-x   13 root root    0 2022-09-14 15:16:15 sys\r\n' ]}

    file_list = []
    # print("0",result['data'][0])
    # if "无法访问" in result['data'][0] or not result or result['status'] != 0:
    if not result or result['status'] != 0:
        # write_log("错误，主机: {}，日志目录不存在！".format(host), "red")
        return False
        # return render(request, "log_list.html", {"id": id, "file_list": file_list, "error_info": error_info})

    # 删除头部信息
    del result['data'][0]
    # print("结果0",result['data'])
    ### {'status': 0, 'data': ['dr-xr-xr-x   13 root root    0 2022-09-14 15:16:15 sys\r\n' ]}

    for i in result['data']:
        # i = i.decode('utf-8').strip()
        i = i.strip()
        # print(i)

        cut_str = i.split()  # 以\n为分隔符
        # print(cut_str)
        file_dict = {}
        if not file_dict.get('type'):
            # 截取第一个字符串
            f_prefix = cut_str[0][0]

            # 判断是否为文件夹
            if f_prefix == 'd':
                f_type = "dir"
            else:
                f_type = "file"

            # print("f_type",f_type)
            file_dict['type'] = f_type

        if not file_dict.get('size'):
            file_dict['size'] = cut_str[4]

        if not file_dict.get('time'):
            file_dict['time'] = "%s %s" % (cut_str[5], cut_str[6])

        # 文件名
        file_name = cut_str.pop()
        # 文件后缀
        file_suffix = file_name.split('.').pop()

        if not file_dict.get('name'):
            file_dict['name'] = file_name

        # 判断是否可以实时查看日志
        if not file_dict.get('view'):
            if file_suffix == 'log':
                file_dict['view'] = '1'
            else:
                file_dict['view'] = '0'

        file_list.append(file_dict)

    # print(file_list)
    return file_list


def fuzzy_finder(key, data):
    """
    模糊查找器
    :param key: 关键字
    :param data: 数据
    :return: list
    """
    # 结果列表
    suggestions = []
    # 非贪婪匹配，转换 'djm' 为 'd.*?j.*?m'
    # pattern = '.*?'.join(key)
    pattern = '.*%s.*' % (key)
    # print("pattern",pattern)
    # 编译正则表达式
    regex = re.compile(pattern)
    for item in data:
        # print("item",item['name'])
        # 检查当前项是否与regex匹配。
        match = regex.search(item['name'])
        if match:
            # 如果匹配，就添加到列表中
            suggestions.append(item)

    # print(suggestions)
    return suggestions


def get_search_log_list(host, dir, key):
    """
    获取搜索日志列表
    :param host: ip地址
    :param dir: 目录名
    :param dir: 关键字
    :return: dict
    {
        "access.log": {
            "type": "dir",
            "size": "123",
            "name": "access.log",
        },
        "access.log.gz": {
            "type": "dir",
            "size": "123",
            "name": "access.log",
        },
    }
    """
    # print(dir)
    result = get_log_list(host, dir)
    if not result:
        return False

    # 搜索关键字
    search_dict = fuzzy_finder(key, result)

    if not search_dict:
        return {}
    else:
        return search_dict


def get_dir_json(request):
    """
    目录列表json，供前端ajax调用
    :param request:
    :return:
    """
    res = BaseResponse()  # 初始化返回值
    if request.method == "POST":
        ip = request.POST.get('ip')
        dir_temp = request.POST.get('dir')
        key = request.POST.get('key')

        dir = dir_temp.replace("//", "/")
        # print(dir)  # 从前端html 传过来的 最初始的 值

        if key:

            log_list = get_search_log_list(ip, dir, key)

            # 分页a标签的herf前缀
            herf_prefix = "?ip={}&dir={}&search={}".format(ip, dir, key)
        else:
            log_list = get_log_list(ip, dir)
            # 分页a标签的herf前缀
            herf_prefix = "?ip={}&dir={}".format(ip, dir)

        # print("log_list", log_list)
        if log_list is False:
            res.code = 500
            res.error = "获取目录列表失败"
        elif log_list == []:
            res.code = 500
            res.error = "目录列表为空"
        elif log_list == {}:
            res.code = 500
            res.error = "搜索结果为空"
        else:
            res.data = log_list
            res.url = "/view_dir/%s" % (herf_prefix)
    else:
        res.code = 500
        res.error = "非法请求"

    return HttpResponse(json.dumps(res.__dict__))


# rsync 选择 远程主机
def rsyncremotehostshow(request):
    allremoteHosts = models.RemoteHost.objects.values()
    # print(allremoteHosts)
    data = {'data': list(allremoteHosts)}  # 元组 转列表

    return JsonResponse(data)


def rsyncall(request):
    return render(request, 'rsync/rsyncall.html')


# 查询所有数据
def selectallrsync(request):
    nowPage = request.GET.get("page")
    limit = request.GET.get("limit")
    if limit == "":
        limit = 5

    allraync_list = models.allrsyncdate.objects.values()
    paginator = Paginator(list(allraync_list), int(limit))
    # print(list(alluser_list))
    if int(nowPage) >= int(paginator.num_pages):
        nowPage = paginator.num_pages
    try:
        page = paginator.page(nowPage)
    except:
        page = paginator.page(paginator.num_pages)

    data = {'data': list(page), 'code': "200", 'count': paginator.count, 'page': page.number, 'limit': page.__len__(),
            'pages': paginator.num_pages

            }
    return JsonResponse(data)  # 返回所有的数据，和每页显示多好行，以及页数的设置


# 添加
# 添加用户
def addrsync(request):
    if request.method == 'POST':
        source_ip = request.POST.get("remotehostLY", None)  # 获取html传过来的值
        source_path = request.POST.get("path_laiyuan", None)
        destination_ip = request.POST.get("remotehostMD", None)
        destination_path = request.POST.get("path_mudi", None)

        if models.allrsyncdate.objects.create(source_ip=source_ip, source_path=source_path,
                                              destination_ip=destination_ip,
                                              destination_path=destination_path):
            return JsonResponse({"code": 200, "msg": "添加成功"}, safe=False)
        else:
            return JsonResponse({"code": 300, "msg": "添加失败"}, safe=False)


# 删除 rsync_delete_all
# 批量删除
def rsync_delete_all(request):
    # 先判断发过来的是否是post请求
    if request.method == "POST":
        # 得到要删除的ids列表 值
        values = request.POST.getlist('ids')

        # 好像传过来的 values  是 ['3,5,7']  这样的形式，我们需要进一步的细化
        # print(values[0])
        # print(type(values[0]))
        # print(values[0].split(','))
        # print(type(values[0].split(',')))

        new_list = values[0].split(',')
        # print(new_list)

        for i in range(len(new_list)):
            # 如果id不为空，获取该字段，并将其删除，我们只删除book表，publisher表不变
            deleteID = int(new_list[i])
            book_obj = models.allrsyncdate.objects.get(id=deleteID)
            book_obj.delete()
        # 删除成功返回显示页
        # return redirect('/curd/')
        data = {

            'code': "200",

        }
        return JsonResponse(data)


# 模糊 查询 searchrsync

def searchrsync(request):
    source_ip = request.GET.get("source_ip")
    destination_ip = request.GET.get("destination_ip")
    nowPage = request.GET.get("page")
    limit = request.GET.get("limit")

    # print(user_email,user_name)
    if limit == "":
        limit = 5

    if source_ip != "":
        user_list = models.allrsyncdate.objects.values().filter(source_ip__contains=source_ip)
    if destination_ip != "":
        user_list = models.allrsyncdate.objects.values().filter(destination_ip__contains=destination_ip)
    paginator = Paginator(list(user_list), int(limit))

    if int(nowPage) >= int(paginator.num_pages):
        nowPage = paginator.num_pages
    try:
        page = paginator.page(nowPage)
    except:
        page = paginator.page(paginator.num_pages)
    data = {'data': list(page), 'code': "200", 'count': paginator.count, 'page': page.number, 'limit': page.__len__(),
            'pages': paginator.num_pages

            }
    return JsonResponse(data)


result = {'status': 1, 'data': None}  # 返回结果   这个好像没有使用到，可以删除的

def rsyncstart(request):
    # 先判断发过来的是否是post请求
    if request.method == "POST":
        # 得到要删除的ids列表 值
        remotehostLY = request.POST.get('remotehostLY')

        path_laiyuan = request.POST.get('path_laiyuan')
        remotehostMD = request.POST.get('remotehostMD')
        path_mudi = request.POST.get('path_mudi')

        # print(remotehostLY,path_laiyuan,remotehostMD,path_mudi)
        sshrsync(remotehostLY, path_laiyuan, remotehostMD, path_mudi)

        msg_temp = remotehostLY + ": " + path_laiyuan + " ---> " +  remotehostMD + ": " + path_mudi + " 该任务开始执行 "
        # print(result)
        # 删除成功返回显示页
        # return redirect('/curd/')
        data = {

            'code': "200",
            'msg': msg_temp,

        }
        return JsonResponse(data)





# ssh 连接
def __sshrsync(remotehostLY, path_laiyuan, remotehostMD, path_mudi):

    try:


        remote_user_id = models.RemoteHost.objects.filter(ip=remotehostLY).values('remote_user_id')[0]['remote_user_id']
        remote_port = models.RemoteHost.objects.filter(ip=remotehostLY).values('port')[0]['port']
        remote_user_name = models.RemoteUser.objects.filter(name=remote_user_id).values('username')[0]['username']
        remote_user_password = models.RemoteUser.objects.filter(name=remote_user_id).values('password')[0][
            'password']

        r_password_decrypt = RsaCrypto().decrypt(remote_user_password)['message']  # 解密 password

        # print(remotehostLY,remote_user_name,r_password_decrypt)

        MD_remote_user_id = models.RemoteHost.objects.filter(ip=remotehostMD).values('remote_user_id')[0]['remote_user_id']
        MD_remote_port =  models.RemoteHost.objects.filter(ip=remotehostMD).values('port')[0]['port']
        # MD_remote_user_name =  models.RemoteUser.objects.filter(name=MD_remote_user_id).values('username')[0]['username']
        # MD_remote_user_password = models.RemoteUser.objects.filter(name=MD_remote_user_id).values('password')[0]['password']
        # MD_r_password_decrypt = RsaCrypto().decrypt(MD_remote_user_password)['message']  # 解密 password

        rsync_id_rw = models.allrsyncdate.objects.filter(
            Q(source_ip=remotehostLY) and Q(source_path=path_laiyuan) and Q(destination_ip=remotehostMD) and Q(
                destination_path=path_mudi)).values('id')[0]['id']

        ssh_client = paramiko.SSHClient()  # 实例化一个
        ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())  # 相当于 linux的 自动添加 主机策略
        ssh_client.connect(hostname=remotehostLY, port=remote_port, username=remote_user_name, password=r_password_decrypt)

    except Exception as e:
        models.allrsyncdate.objects.filter(pk=rsync_id_rw).update( rsync_result_sc="源ip地址ssh连接失败")
        pass

    else:
        cmd = " yum install rsync -y && rsync -az " + path_laiyuan + " -e\"ssh -p " + str(MD_remote_port) + "\"  "   + remotehostMD + ":" + path_mudi
        # print(cmd)
        stdin, stdout, stderr = ssh_client.exec_command(cmd, get_pty=True)
        Stdout_xx = str(stdout.read().decode())



        # print(rsync_id_rw)

        # stdin, stdout, stderr = ssh_client.exec_command(cmd, get_pty=True)
        # Stdout_xx = stdout.readlines()
        print(Stdout_xx)

        channel = stdout.channel  # # 执行状态,0 表示成功，1 表示失败
        status_values = channel.recv_exit_status()

        # print(status_values)

        # 修改数据库里的 信息
        models.allrsyncdate.objects.filter(pk=rsync_id_rw).update(rsync_result_value=status_values, rsync_result_sc=Stdout_xx)

        # 修改返回结果
        # result['status'] = status_values
        # result['data'] = Stdout_xx
        # print(result)

    finally:
        ssh_client.close()  # 关闭连接


def sshrsync(remotehostLY, path_laiyuan, remotehostMD, path_mudi):

    Start_sshrsync = threading.Thread(target=__sshrsync, args=(remotehostLY, path_laiyuan, remotehostMD, path_mudi))
    Start_sshrsync.start()



